﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using XQ.Framework.core;

namespace XQ.Framework.io.image
{
    /// <summary>
    ///用于生成多种样式的验证码
    ///王俊 wjzyrf@gmail.com
    /// </summary>
    public class DrawVerifyCode
    {
        #region 各种参数

        private HttpResponse response = null;
        private int width = 110;
        private int height = 40;
        private readonly Random ran = new Random();
        private delegate string randomChar();

        /// <summary>
        /// 噪线的条数，默认5条
        /// </summary>
        private int lineCount = 5;

        public int LineCount
        {
            get { return lineCount; }
            set { lineCount = value; }
        }

        private string code = string.Empty;
        public string Code
        {
            get { return code; }
            set { code = value; }
        }

        private int codeType = 1;
        /// <summary>
        /// 设置验证码的类型，
        /// 1是英文数字组合，
        /// 2是数字，
        /// 3是中文,
        /// 其他值默认为英文数字组合
        /// 【注意，服务器必须安装中文字符集才可以使用中文验证码】
        /// </summary>
        public int CodeType
        {
            get { return codeType; }
            set { codeType = value; }
        }
        private int fontSize = 15;
        /// <summary>
        /// 字体大小，【默认15px】为了使字符居中显示，请设置一个合适的值
        /// </summary>
        public int FontSize
        {
            get { return fontSize; }
            set { fontSize = value; }
        }
        private int charCount = 4;
        /// <summary>
        /// 验证码字符数
        /// </summary>
        public int CharCount
        {
            get { return charCount; }
            set { charCount = value; }
        }
        /// <summary>
        /// 构造函数，设置response对象
        /// </summary>
        /// <param name="res"></param>
        public DrawVerifyCode(HttpResponse res)
        {
            this.response = res;
        }
        /// <summary>
        /// 设置高度
        /// </summary>
        /// <param name="Height"></param>
        public void setHeight(int Height)
        {
            this.height = Height;
        }
        /// <summary>
        /// 设置宽度
        /// </summary>
        /// <param name="Width"></param>
        public void setWidth(int Width)
        {
            this.width = Width;
        }
        /// <summary>
        /// 设置验证码中的字符数
        /// </summary>
        /// <param name="count"></param>
        public void setCharCount(int count)
        {
            this.charCount = count;
        }

        #endregion

        #region 主体程序
        public byte[] getImageBytes()
        {
            Bitmap bm = null;
            Graphics g = null;
            MemoryStream ms = null;
            try
            {
                randomChar randomchar;
                //判断使用中文或者英文
                switch (codeType)
                {
                    case 1: randomchar = randomChar_en; break;
                    case 2: randomchar = randomChar_num; break;
                    case 3: randomchar = randomChar_cn; break;
                    default: randomchar = randomChar_en; break;
                }
                //获取随机字体，颜色
                bm = new Bitmap(width, height);
                g = Graphics.FromImage(bm);
                g.Clear(Color.White);
                ms = new MemoryStream();
                //判断是否画噪线
                if (lineCount > 0)
                {
                    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                    for (int k = 0; k < lineCount; k++)
                    {
                        x1 = ran.Next(width);
                        y1 = ran.Next(height);
                        x2 = ran.Next(width);
                        y2 = ran.Next(height);
                        g.DrawLine(new Pen(randomColor()), x1, y1, x2, y2);
                    }
                }
                //画验证码
                for (int i = 0; i < charCount; i++)
                {
                    string c = randomchar();
                    this.code += c;//把验证码保存起来
                    float x = 0, y = 0;
                    getCharPosition(i + 1, ref x, ref y);//计算字符的位置
                    g.DrawString(c, randomFont(), new SolidBrush(randomColor()), x, y);
                }
                bm.Save(ms, ImageFormat.Png);
                return ms.ToArray();
                /*
                byte[] bs = ms.ToArray();
                response.OutputStream.Write(bs, 0, bs.Length);
                 * */
            }
            catch (Exception e)
            {
                return null;
            }
            finally
            {
                if (bm != null)
                {
                    bm.Dispose();
                }
                if (g != null)
                {
                    g.Dispose();
                }
                if (ms != null)
                {
                    ms.Dispose();
                }
            }
        }
        /// <summary>
        /// 画验证码
        /// </summary>
        public void drawVerifyCode()
        {
            try
            {
                byte[] b = getImageBytes();
                if (b == null || b.Length == 0) { return; }
                response.Buffer = false;
                response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0);
                response.Expires = 0;
                response.CacheControl = "no-cache";
                response.AppendHeader("Pragma", "No-Cache");
                response.ContentType = "image/Png";
                response.BinaryWrite(b);
                /*
                byte[] bs = ms.ToArray();
                response.OutputStream.Write(bs, 0, bs.Length);
                 * */
            }
            catch (Exception err)
            {
                throw err;
            }
        }

        #endregion

        #region 各种随机，各种算法
        /// <summary>
        /// 计算字符的位置
        /// </summary>
        /// <param name="i">第几个字符</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        private void getCharPosition(int i, ref float x, ref float y)
        {
            float spaceX = (width - charCount * fontSize) / (charCount + 1);
            float spaceY = (height - fontSize) / 2;
            x = spaceX * i + fontSize * (i - 1);
            y = spaceY;
        }
        /// <summary>
        /// 产生随机英文数字字符
        /// </summary>
        private string randomChar_en()
        {
            char[] chars = { 'a','b','c','d','e','f','g','h','i','j','k','m','n'
                           ,'p','q','r','s','t','v','w','x','y','z'
                       ,'1','2','3','4','5','6','7','8','9','0'};
            return chars[ran.Next(chars.Length)].ToString();
        }
        /// <summary>
        /// 产生随机中文字符
        /// </summary>
        /// <returns></returns>
        private string randomChar_cn()
        {
            Encoding gb = Encoding.GetEncoding("gb2312");
            //调用函数产生4个随机中文汉字编码
            object[] bytes = CreateRegionCode(1);
            //根据汉字编码的字节数组解码出中文汉字
            return gb.GetString((byte[])Convert.ChangeType(bytes[0], typeof(byte[])));
        }
        /// <summary>
        /// 获取随机的中文编码
        /// </summary>
        /// <param name="strlength"></param>
        /// <returns></returns>
        private object[] CreateRegionCode(int strlength)
        {
            //定义一个字符串数组储存汉字编码的组成元素
            string[] Base = new String[16] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
            //这里不要定义随机对象，因为每请求一次都会重新生成随机对象，这样就会出现重复的字符串
            //Random ran = new Random();
            //定义一个object数组用来
            object[] bytes = new object[strlength];
            /*每循环一次产生一个含两个元素的十六进制字节数组，并将其放入bject数组中
             每个汉字有四个区位码组成
             区位码第1位和区位码第2位作为字节数组第一个元素
             区位码第3位和区位码第4位作为字节数组第二个元素
            */
            int r1, r2, r3, r4;
            for (int i = 0; i < strlength; i++)
            {
                //区位码第1位
                r1 = ran.Next(11, 14);
                string str_r1 = Base[r1].Trim();
                //区位码第2位
                if (r1 == 13)
                {
                    r2 = ran.Next(0, 7);
                }
                else
                {
                    r2 = ran.Next(0, 16);
                }
                string str_r2 = Base[r2].Trim();
                //区位码第3位
                r3 = ran.Next(10, 16);
                string str_r3 = Base[r3].Trim();
                //区位码第4位
                if (r3 == 10)
                {
                    r4 = ran.Next(1, 16);
                }
                else if (r3 == 15)
                {
                    r4 = ran.Next(0, 15);
                }
                else
                {
                    r4 = ran.Next(0, 16);
                }
                string str_r4 = Base[r4].Trim();
                //定义两个字节变量存储产生的随机汉字区位码
                byte byte1 = Convert.ToByte(str_r1 + str_r2, 16);
                byte byte2 = Convert.ToByte(str_r3 + str_r4, 16);
                //将两个字节变量存储在字节数组中
                byte[] str_r = new byte[] { byte1, byte2 };
                //将产生的一个汉字的字节数组放入object数组中
                bytes[i] = str_r;
            }
            return bytes;
        }
        /// <summary>
        /// 返回随机数字
        /// </summary>
        /// <returns></returns>
        private string randomChar_num()
        {
            char[] chars = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
            return chars[ran.Next(chars.Length)].ToString();
        }
        /// <summary>
        /// 获取随机字体
        /// </summary>
        /// <returns></returns>
        private Font randomFont()
        {
            if (codeType == 3)
            {
                fontSize = 15;
            }
            string[] fonts = { "Times New Roman", "MS Mincho", "Gungsuh", "PMingLiU", "Impact" };
            return new Font(fonts[ran.Next(fonts.Length)], fontSize);
        }
        /// <summary>
        /// 获取随机颜色
        /// </summary>
        /// <returns></returns>
        private Color randomColor()
        {
            Color[] colors = { Color.Purple, Color.Black, Color.DarkBlue, Color.DarkRed };
            return colors[ran.Next(colors.Length)];
        }

        #endregion
    }
}

